package com.zzw.junit5demo;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

/**
 * @author zzw
 * @version 1.0
 * @title DynamicTestsDemo
 * @description <动态测试>
 * @datetime 2023-07-05 23:26
 */
class DynamicTestsDemo {
    private static final Logger logger = LoggerFactory.getLogger(DynamicTestsDemo.class);


    @BeforeAll
    public static void beforeAll(){
        logger.info("===========所有用例之前执行===========");
    }

    @AfterAll
    public static void afterAll(){
        logger.info("===========所有用例之后执行===========");
    }

    @BeforeEach
    public void beforeEach(){
        logger.info("+++++++++++每条用例之前执行+++++++++++");
    }

    @AfterEach
    public void afterEach(){
        logger.info("+++++++++++每条用例之后执行+++++++++++\n\n");
    }


    boolean isPalindrome(String str){
        return true;
    }
    // 将会抛出 JUnitException!
    @TestFactory
    List<String> dynamicTestsWithInvalidReturnType() {
        logger.info("dynamicTestsWithInvalidReturnType");
        return Arrays.asList("Hello");
    }

    @TestFactory
    Collection<DynamicTest> dynamicTestsFromCollection() {
        logger.info("dynamicTestsFromCollection");
        return Arrays.asList(
            dynamicTest("1st dynamic test", () -> assertTrue(isPalindrome("madam"))),
            dynamicTest("2nd dynamic test", () -> assertEquals(4, Math.multiplyExact(2, 2)))
        );
    }

    @TestFactory
    Iterable<DynamicTest> dynamicTestsFromIterable() {
        logger.info("dynamicTestsFromIterable");
        return Arrays.asList(
            dynamicTest("3rd dynamic test", () -> assertTrue(isPalindrome("madam"))),
            dynamicTest("4th dynamic test", () -> assertEquals(4, Math.multiplyExact(2, 2)))
        );
    }

    @TestFactory
    Iterator<DynamicTest> dynamicTestsFromIterator() {
        logger.info("dynamicTestsFromIterator");
        return Arrays.asList(
            dynamicTest("5th dynamic test", () -> assertTrue(isPalindrome("madam"))),
            dynamicTest("6th dynamic test", () -> assertEquals(4, Math.multiplyExact(2, 2)))
        ).iterator();
    }

    @TestFactory
    DynamicTest[] dynamicTestsFromArray() {
        logger.info("dynamicTestsFromArray");
        return new DynamicTest[] {
            dynamicTest("7th dynamic test", () -> assertTrue(isPalindrome("madam"))),
            dynamicTest("8th dynamic test", () -> assertEquals(4, Math.multiplyExact(2, 2)))
        };
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromStream() {
        logger.info("dynamicTestsFromStream");
        return Stream.of("racecar", "radar", "mom", "dad")
            .map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text))));
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromIntStream() {
        logger.info("dynamicTestsFromIntStream");
        // 为前10个偶数整数生成测试
        return IntStream.iterate(0, n -> n + 2).limit(10)
            .mapToObj(n -> dynamicTest("test" + n, () -> assertEquals(0, n % 2)));
    }

    @TestFactory
    Stream<DynamicTest> generateRandomNumberOfTestsFromIterator() {
        logger.info("generateRandomNumberOfTestsFromIterator");
        // 生成0到100之间的随机正整数，直到遇到可被7整除的数字为止。
        Iterator<Integer> inputGenerator = new Iterator<Integer>() {
            final Random random = new Random();
            int current;

            @Override
            public boolean hasNext() {
                current = random.nextInt(100);
                return current % 7 != 0;
            }

            @Override
            public Integer next() {
                return current;
            }
        };

        // 生成显示名称，如：input:5、input:37、input:85等。
        Function<Integer, String> displayNameGenerator = (input) -> "input:" + input;

        // 根据当前输入值执行测试。
        ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0);

        // 返回一个动态测试流。
        return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromStreamFactoryMethod() {
        logger.info("dynamicTestsFromStreamFactoryMethod");

        // Stream of palindromes to check
        Stream<String> inputStream = Stream.of("racecar", "radar", "mom", "dad");

        // Generates display names like: racecar is a palindrome
        Function<String, String> displayNameGenerator = text -> text + " is a palindrome";

        // Executes tests based on the current input value.
        ThrowingConsumer<String> testExecutor = text -> assertTrue(isPalindrome(text));

        // Returns a stream of dynamic tests.
        return DynamicTest.stream(inputStream, displayNameGenerator, testExecutor);
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromStreamFactoryMethodWithNames() {
        logger.info("dynamicTestsFromStreamFactoryMethodWithNames");

        // Stream of palindromes to check
    //     Stream<Named<String>> inputStream = Stream.of(
    //             named("racecar is a palindrome", "racecar"),
    //             named("radar is also a palindrome", "radar"),
    //             named("mom also seems to be a palindrome", "mom"),
    //             named("dad is yet another palindrome", "dad")
    //         );
    //     return DynamicTest.stream(Stream.of(),
    //         text -> assertTrue(isPalindrome(text)));
        return Stream.<DynamicTest>builder().build();
    }

    @TestFactory
    Stream<DynamicNode> dynamicTestsWithContainers() {
        logger.info("dynamicTestsWithContainers");

        return Stream.of("A", "B", "C")
            .map(input -> dynamicContainer("Container " + input, Stream.of(
                dynamicTest("not null", () -> assertNotNull(input)),
                dynamicContainer("properties", Stream.of(
                    dynamicTest("length > 0", () -> assertTrue(input.length() > 0)),
                    dynamicTest("not empty", () -> assertFalse(input.isEmpty()))
                ))
            )));
    }

    @TestFactory
    DynamicNode dynamicNodeSingleTest() {
        logger.info("dynamicNodeSingleTest");
        return dynamicTest("'pop' is a palindrome", () -> assertTrue(isPalindrome("pop")));
    }

    @TestFactory
    DynamicNode dynamicNodeSingleContainer() {
        logger.info("dynamicNodeSingleContainer");

        return dynamicContainer("palindromes",
            Stream.of("racecar", "radar", "mom", "dad")
                .map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text)))
        ));
    }

}